# 类与对象的创建
# 定义与调用
import abc


class StudentSetUp(object):
    # 对象公共属性
    school_name = '老男孩'

    # 给学生添加独有数据
    def __init__(self, name, age, hobby):
        self.name = name
        self.age = age
        self.hobby = hobby

    # 对象公共功能
    def python(self):
        pass

    def linux(self):
        pass


# 动态、静态方法
class StudentDDL(StudentSetUp):
    def __init__(self, name, age, hobby):
        super().__init__(name, age, hobby)

    # 直接在类中定义函数
    def curricula_variable(self):
        print(f'{self.name}正在选课')

    # 绑定给类的函数
    @classmethod
    def func(cls):
        print('我是绑定给类的功能', cls)

    @staticmethod
    def func2(*args, **kwargs):
        print(fr'我是被[staticmethod]修饰的普通函数，不管谁调用我都需要手动传参 {args, kwargs}')


# 继承
class Father:
    money = 999999999
    house = '老北京四合院'


class Son(Father):
    pass


# 派生
class People:
    school = '清华大学'

    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age


class Teacher(People):
    def __init__(self, name, sex, age, title):
        super().__init__(name, sex, age)
        self.title = title

    def teach(self):
        print(f'{self.name}是老师')


# 封装
# 隐藏
class MyClass:
    school_name = '老女孩大学'
    __age = 18

    def __choice_course(self):
        print('老北鼻正在选课')


# 调用
class Person:
    def __init__(self, name, age, hobby):
        self.__name = name
        self.__age = age
        self.__hobby = hobby

    def get_info(self):
        print(f"""
        姓名：{self.__name}
        年龄：{self.__age}
        爱好：{self.__hobby}
        """)

    def set_name(self, new_name):
        if len(new_name) == 0:
            raise ValueError('你好歹写点东西')
        if new_name.isdigit():
            raise ValueError('名字不能是数字')
        self.__name = new_name


# 伪装
class PersonAndProperty(object):
    def __init__(self, name, height, weight):
        self.name = name
        self.height = height
        self.weight = weight

    @property
    def bmi(self):
        return self.weight / (self.height ** 2)


class Foo:
    def __init__(self, val):
        self.__NAME = val

    @property
    def name(self):
        return self.__NAME

    @name.setter
    def name(self, value):
        if not isinstance(value, str):
            raise TypeError('%s must be str' % value)
        self.__NAME = value

    @name.deleter
    def name(self):
        raise PermissionError('can not delete')


# 多态
class Animal(object):
    def spark(self):
        pass


class Cat(Animal):
    def spark(self):
        print('喵喵喵')


class Dog(Animal):
    def spark(self):
        print('汪汪汪')


class Pig(Animal):
    def spark(self):
        print('哼哼哼')


class AnimalABS(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def talk(self):
        pass


class CatABS(AnimalABS):
    def talk(self):
        pass


class User_cls_info(object):
    def __init__(self, name, age, gender, hobby):
        self.__name = name
        self.__age = age
        self.__gender = gender
        self.__hobby = hobby

    def check_info(self):
        for i in self.__dict__:
            v = getattr(self, i)
            i_1 = i.split('__')[1]
            print(f'属性名：{i_1} 数据值：{v}')

    def alter_info(self):
        alter_k = input('请输入需要修改的数据名>>>: ').strip()
        alter_v = input('请输入修改后的数据>>>: ').strip()

        alter_k_ = f'_User_cls_info__{alter_k}'
        if alter_k_ not in self.__dict__:
            print('该属性不存在')
            return
        if len(alter_v) <= 0:
            print('数据值不得为空')
            return
        setattr(self, alter_k_, alter_v)
        print(f'修改成功,{alter_k},已被修改为{alter_v}')

    def add_info(self):
        add_k = input('输入需要增加的数据名>>>: ').strip()
        add_v = input('输入对应的数据值>>>: ').strip()
        if len(add_k) <= 0 or len(add_v) <= 0:
            print('数据值或数据名不得为空')
            return
        add_k_ = f'_User_cls_info__{add_k}'
        setattr(self, add_k_, add_v)
        print(f'已成功添加, 数据名：{add_k}, 数据值：{add_v}')

    def del_info(self):
        del_k = input('输入需要删除的数据>>>: ').strip()
        del_k = f'_User_cls_info__{del_k}'
        if del_k not in self.__dict__:
            print('该数据名不存在')
            return
        delattr(self, del_k)
        print(f'数据{del_k}已删除')

    def run(self):
        while True:
            print("""
            1. 查看用户信息>>>：check_info
            2. 修改用户信息>>>：alter_info
            3. 增加用户信息>>>：add_info
            4. 删除用户信息>>>：del_info
            """)
            user_choice = input('输入需要执行的功能>>>: ').strip()
            if hasattr(self, user_choice):
                getattr(self, user_choice)()
            if user_choice == 'break':
                return
            else:
                print('该功能不存在')


# 面向对象常用方法
class ObjectMethod(object):
    # 构造函数 创建实例时自动触发
    def __init__(self, height, weight):
        self.__height = height
        self.__weight = weight

    # 对象被执行打印时自动触发，该方法必须return字符串，否则异常  toString
    def __str__(self):
        return f'重量：{self.__height} 高度：{self.__weight}'

    # 对象括号调用时触发
    def __call__(self, *args, **kwargs):
        # self 调用者本身
        # args 位置形参
        # kwargs 关键字形参
        print(f'重量：{self.__height} 高度：{self.__weight}')

    def __getattr__(self, item):
        # self：调用者本身
        # item: 调用者名字
        pass

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        # with上下文管理运行完毕后自动触发(子代码体结束)
        pass

    def __bool__(self):
        # 当调用 bool(obj) 时，会调用 __bool__()方法，返回true 或 False
        return False

    def __new__(cls, *args, **kwargs):
        print('__new__()方法被调用了')
        print('这个时*args', *args)
        print('这个数**kwargs', **kwargs)
        """
        1. __new__(cls, [...]) 是在一个对象实例化的时候所调用的第一个方法，所以他才是真正意义上的构造函数
        2. 它的第一个参数是这个类，其他的参数是用来直接传递给 __init__ 方法
        3. __new__ 决定是否要使用该 __init__ 方法, 因为 __new__ 可以调用其他类的构造方法或者直接返回
        4. __new__ 主要用于继承一个不可变的类型比如一个 tuple 或者 string
        """

    def __del__(self):
        pass
        """
        __del__是析构方法，当对象的引用计数变为0时，这个对象会被销毁掉，此时就会调用__del__方法
        """


def student_setup_test():
    # 调用对象
    obj1 = StudentSetUp('kangkang1', 18, 'python')
    obj2 = StudentSetUp('kangkang2', 20, 'java')
    obj3 = StudentSetUp('kangkang3', 22, 'c')
    # 查看对象内部数据
    print(obj1.__dict__)
    # 调用对象公共数据
    print(obj1.school_name)
    # 对象属性修改
    obj1.hobby = 'goLang'
    obj1.pwd = '123456'
    print(obj1.__dict__)


def student_ddl_test():
    # 直接定义
    # 类调用(需要先产生对象，然后将产生的对象当作参数传入)
    obj1 = StudentDDL('kangkang1', 18, 'python')
    StudentDDL.curricula_variable(obj1)
    # 对象调用(直接通过类实例进行函数调用，无需额外传参)
    obj1.curricula_variable()
    # classMethod修饰
    # 类可直接调用
    StudentDDL.func()
    # 对象调用
    obj1.func()
    # staticMethod修饰
    StudentDDL.func2('普通函数')
    obj1.func2(1, 2, 3, a='apple', b='banana')


def extend_test():
    # 继承
    son_obj = Son()
    print(son_obj.money)
    obj = Teacher('lili', 'female', 28, '高级教师')
    print(obj.__dict__)
    # 封装
    obj2 = Person('kangkang', 18, 'python')
    obj2.get_info()
    p1 = PersonAndProperty('jason', 1.83, 78)
    print(p1.bmi)
    f = Foo('jason')
    print(f.name)
    f.name = 'jason123'
    print(f.name)
    del f.name


def invoke():
    # hasattr 判断目标类是否具有该属性或函数
    str_temp = input('输入属性或方法名：')
    print(hasattr(AnimalABS, str_temp))
    # getattr 获取目标类对应的属性或函数，未找到则抛异常
    print(getattr(StudentSetUp, 'school_name'))
    try:
        print(getattr(StudentSetUp, 'school_name1'))
    except AttributeError as e:
        print(f'Error: 未找到该属性或是函数')
    # setattr 根据字符串，给对象设置/修改数据
    setattr(StudentSetUp, 'school_name', '小男孩')
    print(StudentSetUp.__dict__)
    # delattr 根据字符串，删除对象属性
    delattr(StudentSetUp, 'school_name')
    print(StudentSetUp.__dict__)
    # # 反射 -- 增删改查
    user = User_cls_info('kangkang', 18, 'male', 'read')
    user.run()


def object_method_test():
    o = ObjectMethod(200, 180)
    print(o)
    o()
    print(o.print1)


if __name__ == '__main__':
    object_method_test()
